/*
* libjingle
* Copyright 2004--2005, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*  1. Redistributions of source code must retain the above copyright notice,
*     this list of conditions and the following disclaimer.
*  2. Redistributions in binary form must reproduce the above copyright notice,
*     this list of conditions and the following disclaimer in the documentation
*     and/or other materials provided with the distribution.
*  3. The name of the author may not be used to endorse or promote products
*     derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef TALK_SESSION_STR_STRSESSIONCLIENT_H_
#define TALK_SESSION_STR_STRSESSIONCLIENT_H_

#include <string>
#include <vector>
#include <map>
#include <algorithm>

#include "talk/base/sigslot.h"
#include "talk/base/sigslotrepeater.h"
#include "talk/base/messagequeue.h"
#include "talk/base/thread.h"
#include "talk/base/network.h"
#include "talk/p2p/base/sessionmanager.h"
#include "talk/p2p/base/session.h"
#include "talk/p2p/base/sessionclient.h"
#include "talk/p2p/base/sessiondescription.h"
#include "talk/p2p/base/parsing.h"

#include "talk/p2p/client/sessionmanagertask.h"
#include "talk/p2p/client/httpportallocator.h"

#include "talk/xmpp/xmppclient.h"

#include "talk/examples/crossfirewall/cfcall.h"
#include "talk/examples/crossfirewall/cryptoparams.h"

namespace cricket {

	const int kAutoBandwidth = -1;
	struct RtpHeaderExtension {
		RtpHeaderExtension(const std::string& u, int i) : uri(u), id(i) {}
		std::string uri;
		int id;
		// TODO: SendRecv direction;
	};

	//// Parameters for SRTP negotiation, as described in RFC 4568.
	//struct CryptoParams {
	//	CryptoParams() : tag(0) {}
	//	CryptoParams(int t, const std::string& cs,
	//		const std::string& kp, const std::string& sp)
	//		: tag(t), cipher_suite(cs), key_params(kp), session_params(sp) {}

	//	bool Matches(const CryptoParams& params) const {
	//		return (tag == params.tag && cipher_suite == params.cipher_suite);
	//	}

	//	int tag;
	//	std::string cipher_suite;
	//	std::string key_params;
	//	std::string session_params;
	//};

	class SessionDescription;

	class CFSessionClient : public SessionClient, public sigslot::has_slots<>
	{
	public:
		CFSessionClient(buzz::XmppClient* xmppclient, bool bhttpallocator);
		~CFSessionClient();

		SessionManager* session_manager() const { return session_manager_; }

		SessionDescription* CreateOffer(const std::string& session_type, const std::string& session_name);
		SessionDescription* CreateAnswer(const SessionDescription* offer);

		virtual const SessionDescription* CreateSessionDescription(const buzz::XmlElement* element) { return NULL; }
		buzz::XmlElement* TranslateSessionDescription(const SessionDescription* description);

		Session* CreateSession(std::string name, std::string session_type);
		void DestroySession(Session *session);

		std::string GetSessionChannelName(const SessionDescription* offer);

		void SetHttpAllocatorHosts(const std::string & relay_token, const std::vector<std::string> &relay_addresses,
			const std::vector<talk_base::SocketAddress> &stun_addresses) {
				cricket::HttpPortAllocator* pa = reinterpret_cast<cricket::HttpPortAllocator*>(port_allocator_);
				if(pa) {
					pa->SetStunHosts(stun_addresses);
					pa->SetRelayHosts(relay_addresses);
					pa->SetRelayToken(relay_token);
				}
		}

	public:
		sigslot::signal1<Session *> SignalSessionIncoming;
		sigslot::signal1<Session *> SignalSessionDestroy;

	private:
		void OnSessionState(BaseSession *session, BaseSession::State state);
		void OnSessionCreate(Session *session, bool received_initiate);
		void OnSessionDestroy(Session *session);

		bool ParseContent(SignalingProtocol protocol, const buzz::XmlElement* elem, const ContentDescription** content, ParseError* error);
		bool WriteContent(SignalingProtocol protocol, const ContentDescription* content, buzz::XmlElement** elem, WriteError* error);

		void OnRequestSignaling() { session_manager_->OnSignalingReady(); }

	private:
		PortAllocator*       port_allocator_;
		SessionManager*      session_manager_;
		SessionManagerTask*  session_manager_task_;

		talk_base::Thread*            worker_thread_;
		talk_base::NetworkManager*    network_manager_;

	};

	enum CFType {
		CFTYPE_SESSION_RTP,
		CFTYPE_CHANNEL_NAME,
	};

	class CFContentDescription : public ContentDescription {
	public:
		CFContentDescription() : ssrc_(0), ssrc_set_(false), rtcp_mux_(false),
			bandwidth_(kAutoBandwidth),
			crypto_required_(false),
			rtp_headers_disabled_(false) {}

		virtual CFType type() const = 0;

		uint32 ssrc() const { return ssrc_; }
		bool ssrc_set() const { return ssrc_set_; }
		void set_ssrc(uint32 ssrc) {
			ssrc_ = ssrc;
			ssrc_set_ = true;
		}

		bool rtcp_mux() const { return rtcp_mux_; }
		void set_rtcp_mux(bool mux) { rtcp_mux_ = mux; }

		int bandwidth() const { return bandwidth_; }
		void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; }

		const std::vector<CryptoParams>& cryptos() const { return cryptos_; }
		void AddCrypto(const CryptoParams& params) {
			cryptos_.push_back(params);
		}
		bool crypto_required() const { return crypto_required_; }
		void set_crypto_required(bool crypto) {
			crypto_required_ = crypto;
		}

		bool rtp_headers_disabled() const {
			return rtp_headers_disabled_;
		}
		void set_rtp_headers_disabled(bool disable) {
			rtp_headers_disabled_ = disable;
		}




		const std::vector<RtpHeaderExtension>& rtp_header_extensions() const {
			return rtp_header_extensions_;
		}
		void AddRtpHeaderExtension(const RtpHeaderExtension& ext) {
			rtp_header_extensions_.push_back(ext);
			rtp_header_extensions_set_ = true;
		}
		void ClearRtpHeaderExtensions() {
			rtp_header_extensions_.clear();
			rtp_header_extensions_set_ = true;
		}
		// We can't always tell if an empty list of header extensions is
		// because the other side doesn't support them, or just isn't hooked up to
		// signal them. For now we assume an empty list means no signaling, but
		// provide the ClearRtpHeaderExtensions method to allow "no support" to be
		// clearly indicated (i.e. when derived from other information).
		bool rtp_header_extensions_set() const {
			return rtp_header_extensions_set_;
		}



	protected:
		uint32 ssrc_;
		bool ssrc_set_;
		bool rtcp_mux_;
		int bandwidth_;
		std::vector<CryptoParams> cryptos_;
		bool crypto_required_;
		std::vector<RtpHeaderExtension> rtp_header_extensions_;
		bool rtp_header_extensions_set_;
		bool rtp_headers_disabled_;
	};

	class CFContentDescriptionImpl : public CFContentDescription {
	public:
		struct PreferenceSort {
			bool operator()(int a, int b) { return a > b; }
		};
	};

	class CFRtpContentDescription : public CFContentDescriptionImpl {
	public:
		CFRtpContentDescription() {}

		  virtual CFType type() const { return CFTYPE_SESSION_RTP; }
		  const std::string &lang() const { return lang_; }
		  void set_lang(const std::string &lang) { lang_ = lang; }

	private:
		std::string lang_;
	};

	class CFChannelContentDescription: public CFContentDescriptionImpl{
	public:
		CFChannelContentDescription() {}
		virtual CFType type() const { return CFTYPE_CHANNEL_NAME; }
	};

	// Convenience functions.
	const ContentInfo* GetFirstContent(const SessionDescription* sdesc, CFType type);
	const ContentInfo* GetFirstSessionContent(const SessionDescription* sdesc, CFType& type);
	const ContentInfo* GetFirstChannelContent(const SessionDescription* sdesc);

}  // namespace cricket

#endif  // TALK_SESSION_PHONE_MEDIASESSIONCLIENT_H_
